pub mod c_char_x;
pub mod error;
pub mod extend_c;

use deno_runtime::{
    deno_core::{serde_v8, FastString, JsRuntime, RuntimeOptions},
    deno_napi::v8,
};

use crate::error::{JsEngineError, JsEngineResult};

pub struct JsEngine {
    pub runtime: JsRuntime,
}

impl Default for JsEngine {
    fn default() -> Self {
        let runtime = JsRuntime::new(RuntimeOptions::default());
        Self { runtime }
    }
}

impl JsEngine {
    pub fn eval(&mut self, script: String) -> JsEngineResult {
        let result = self.exec(script);
        match result {
            Ok(value) => JsEngineResult {
                code: 0,
                msg: "ok".to_string(),
                data: Some(value),
            },
            Err(err) => JsEngineResult {
                code: 1,
                msg: err.to_string(),
                data: None,
            },
        }
    }

    fn exec(&mut self, script: String) -> Result<serde_json::Value, JsEngineError> {
        let runtime = &mut self.runtime;

        let script = FastString::from(script.to_string());
        let global = runtime.execute_script("deno-runtime-engine", script)?;
        let mut scope = runtime.handle_scope();
        let local = v8::Local::new(&mut scope, global);

        let result = serde_v8::from_v8::<serde_json::Value>(&mut scope, local)?;
        Ok(result)
    }
}

#[cfg(test)]
mod tests {
    use crate::JsEngine;

    #[test]
    fn test() -> anyhow::Result<()> {
        let mut js_engine = JsEngine::default();
        let result = js_engine.eval("var a=1; var b=2; a + b".to_string());
        println!("{:?}", result);

        Ok(())
    }
}
